<?php
/**
 * Created by laoyu.
 * User: hilaoyu@qq.com
 * Date: 2018/5/3
 * Time: 17:32
 */

namespace Hilaoyu\LaravelExtend\Services\ThirdPartyLogin;


use Hilaoyu\LaravelExtend\Services\ThirdPartyLogin\Drivers\AbstractDriver;
use Hilaoyu\LaravelExtend\Services\ThirdPartyLogin\Drivers\DriverQq;
use Hilaoyu\LaravelExtend\Services\ThirdPartyLogin\Drivers\DriverWechat;
use Hilaoyu\LaravelExtend\Supports\Uuid;
use Hilaoyu\Utils\Traits\ErrorsTrait;
use Hilaoyu\Utils\UtilStr;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;

class ThirdPartyLoginService
{

    use ErrorsTrait;
    /**
     * @return \Illuminate\Support\Collection
     */
    static public function clientAllowedDrivers($driver = null)
    {
        $third_configs = collect(config('third_party_login.drivers', []));
        if (!empty($driver)) {
            $driver = (array)$driver;
        }
        $methods = $third_configs->filter(function ($item) use ($driver) {
            if(!empty($driver) && !in_array(data_get($item,'driver',''),$driver)){
                return false;
            }
            $item_allow_clients = isset($item['allow_clients']) ? (array)$item['allow_clients'] : [];
            if (in_array('all', $item_allow_clients)) {
                return true;
            }

            if (request()->isWechat()) {
                if (in_array('wechat', $item_allow_clients)) {
                    return true;
                }
            } elseif (request()->isWeb()) {
                if (in_array('web', $item_allow_clients)) {
                    return true;
                }

                if (request()->isMobile()) {
                    if (in_array('mobile', $item_allow_clients)) {
                        return true;
                    }
                } else {
                    if (in_array('pc', $item_allow_clients)) {
                        return true;
                    }
                }
            }


            return false;
        });


        return $methods->map(function ($item) {
            return [
                'name' => data_get($item, 'name', ''),
                'driver' => data_get($item, 'driver', ''),
            ];


        });

    }

    /**
     * @param $driver_key
     * @return AbstractDriver|null
     */
    static public function CreateDriver($driver_key){
        $driver = null;
        $driverConfig = config('third_party_login.drivers.'.$driver_key, null);
        if(! ($driverConfig) || !($driver_driver = data_get($driverConfig,'driver',''))){
            return $driver;
        }

        $driver_driver = UtilStr::lower($driver_driver);
        switch ($driver_driver){
            case 'qq':
                return new DriverQq($driverConfig);
                break;
            case 'wechat':
                return new DriverWechat($driverConfig);
                break;
            default:
                break;
        }

        return $driver;
    }

    static public function getProviderConfig($provider,$key=''){
        $key = trim($key);
        if(!$provider || !($provider_config = config('third_party_login.providers.'.$provider,false))){
            return null;
        }

        if($key){
            return data_get($provider_config,$key);
        }
        return $provider_config;
    }

    public function __construct()
    {

    }

    /**
     *  构造第三方登录跳转地址
     * @param $key 第三方登录配置键
     * @param string $guard
     * @param array $params
     * @return bool|mixed
     */
    public function buildLoginUrl($key,$guard, $params = [])
    {
        $key = trim($key);

        if (empty($key) || !($driver = static::CreateDriver($key))) {
            $this->setError('登录方式错误'.$key);
            return false;
        }

        data_set($params,'driver_key',$key);
        data_set($params,'guard',$guard);

        $state = static::StateCacheSave($params);

        if (!$link = $driver->buildLoginUrl($state)) {
            $this->setError($driver->getError());
            return false;
        }


        return $link;


    }


    /**
     * 登录
     * @param $code
     * @param $state
     * @return null
     */
    public function login($code, $state='')
    {

        $code = trim($code);

        if (empty($code)) {
            $this->setError('code 不能为空');
            return null;
        }

        if (empty($state)) {
            $this->setError('state 不能为空');
            return null;
        }

        $stateParams = static::StateCacheGet($state);
        $driver_key = data_get($stateParams,'driver_key','');
        if (empty($driver_key)) {
            $this->setError('驱动配置错误');
            return null;
        }

        if (empty($driver_key) || !($driver = static::CreateDriver($driver_key))) {
            $this->setError('登录方式错误');
            return null;
        }

        $access = $driver->getAuthorization($code);
        if (!$access) {
            $this->setError('code 认证失败');
            $this->setError($driver->getError());
            return false;
        }

        $access_token = $access['access_token'];
        $openid = $driver->getOpenId($access);
        $unionid = $driver->getUnionId();

        /*$openid = 'oM2pWxKwhqAAY25x1C2wcwUHGdCw';
        $unionid = 'ofG_evpGcTz7NcxriFisp0u49YlE';*/


        $driver_name = $driver->getDriverName();
        $user_third = $this->getUserThird($driver_name, $unionid);
        if (!$user_third) {
            return false;
        }

        if (!$user_third->exists) {
            $user_info = $driver->getUserinfo($openid, $access_token);
            /*$user_info = [
                "openid"=>  "onhrWwKQ2mlvwH0I5TRYRX2Gtf90" ,
                "nickname"=>  "笨书生" ,
                "sex"=> 1,
                "language"=>  "zh_CN" ,
                "city"=>  "Banan" ,
                "province"=>  "Chongqing" ,
                "country"=>  "China" ,
                "headimgurl"=>  "http://wx.qlogo.cn/mmopen/vi_32/DYAIOgq83ermm498iaT8hp4JxaPV2asQGUovOhQMo6nILKQEh0GzgBEnWIJmj8RSKPyIDbvGAjvswxIOqlA3zEQ/0" ,
                "unionid"=>  "ofG_evpGcTz7NcxriFisp0u49YlE"
            ];*/

            if (!$user_info) {
                $this->setError('获取用户信息出错');
                $this->setError($driver->getError());
                return false;
            }

            $user_third->fill($user_info);

            if (!$user_third->save()) {
                $this->setError($user_third->getError());
                return false;
            }
        }

        $user_entity = new OauthUsersEntity();

        $user = null;
        if ($bind_user_id && !($user = $user_entity->findByID($bind_user_id))) {
            $this->setError('绑定用户不存在');
            return false;
        }


        if (!$user && (empty($user_third->user_id) || !($user = $user_entity->findByID($user_third->user_id)))) {
            //创建新用户
            $user_data = $this->generateUserDataFromThird($driver, $user_third);

            if (!$user = $user_entity->save($user_data)) {
                $this->setError($user_entity->getError(true));
                return false;
            }
        }

        if (!$user->picture && $user_third->headimgurl) {
            $user->fill(['picture' => $user_third->headimgurl])->save();
        }


        //把用户ID更新到第三方用户表中
        if ($user_third->user_id != $user->_id) {
            $user_third->user_id = $user->_id;
            if (!$user_third->save()) {
                $this->setError($user_third->getError(true));
                return false;
            }
        }

        if ($user->lock_status) {
            $this->setError('用户已锁定');
            return false;
        }
        $user->loginRefresh(session('client_oauth.app_id', ''));

        return $user;

    }

    /**
     * 获取已保存的第三方用户信息
     * @param $type  wechat | qq
     * @param $unionid
     * @return Model|null
     */
    public function getUserThird($type, $unionid)
    {
        $type = trim($type);
        $unionid = trim($unionid);

        if (empty($type)) {
            $this->setError('type不能为空');
            return false;
        }
        if (empty($unionid)) {
            $this->setError('OpenID不能为空');
            return false;
        }

        $model = null;

        switch (strtolower($type)) {
            case 'wechat':
                $model = new OauthUsersThirdWechatModel();
                break;

            case 'qq':
                $model = new OauthUsersThirdQqModel();
                break;

            case 'sina':
                $model = new OauthUsersThirdSinaModel();
                break;

            default:
                $this->setError('暂不支持' . $type);
                break;
        }

        if (!$model) {
            return false;
        }

        if ($user_third = $model->find($unionid)) {
            $model = $user_third;
        }

        return $model;
    }


    /**
     * 从第三方用户数据中提取初始用户信息
     * @param $type
     * @param $third
     * @return array
     */
    protected function generateUserDataFromThird($type, $third)
    {

        $user_data = [
            'account' => $type . '_' . $third['unionid'],
            'pass' => \Base\Util::randString(6),
            'picture' => isset($third['headimgurl']) ? $third['headimgurl'] : '',
            //'date_of_birth' => '' ,
        ];
        switch (strtolower($type)) {
            case 'wechat':
                $user_data['name'] = isset($third['nickname']) ? $third['nickname'] : '';
                $wx_sex = [
                    '0' => '保密',
                    '1' => '男',
                    '2' => '女',
                ];
                $sex = isset($third['sex']) ? intval($third['sex']) : 0;
                $user_data['sex'] = isset($wx_sex[$sex]) ? $wx_sex[$sex] : '保密';

                break;

            case 'qq':
                $user_data['name'] = isset($third['nickname']) ? $third['nickname'] : '';
                $user_data['sex'] = '保密';
                break;

            case 'sina':
                $user_data['name'] = isset($third['name']) ? $third['name'] : '';
                $user_data['sex'] = '保密';

                break;

            default:
                break;
        }

        return $user_data;
    }

    static protected function StateCacheSave($data){
        $id = Uuid::generate();
        $cache_key = 'third_login_state_' . $id;
        Cache::put($cache_key,$data,Carbon::now()->addMinutes(5));
        return $id;
    }

    static public function StateCacheGet($id,$key=''){
        $id = trim($id);
        $key = trim($key);
        if(empty($id)){
            return null;
        }
        $data =  Cache::get('third_login_state_' . $id);

        if('' != $key){
            return data_get($data,$key);
        }

        return $data;
    }

    static public function StateCacheDelete($id){
        $id = trim($id);
        if(empty($id)){
            return null;
        }
        return Cache::forget('third_login_state_' . $id);
    }
}
